﻿
using System;
using System.Linq.Expressions;
using System.Reflection;
using RpcClient.Attr;
using RpcClient.Config;
using RpcClient.Interface;
using RpcClient.Route;
using RpcHelper;
using RpcModel;
using SocketTcpServer.FileUp;
using SocketTcpServer.Interface;

namespace RpcClient.Helper
{
    internal delegate void ExecAction(object target, object[] args);
    internal delegate object ExecFunc(object target, object[] args);

    internal delegate IBasicRes ExecRoute(object target, object[] args);
    internal class RpcRouteHelper
    {
        public static ExecRoute GetExecRoute(MethodInfo method)
        {
            ParameterExpression target = Expression.Parameter(PublicDataDic.ObjectType, "target");
            ParameterExpression arrays = Expression.Parameter(typeof(object[]), "args");
            ParameterInfo[] parameters = method.GetParameters();
            Expression[] list = new Expression[parameters.Length];
            for (int i = 0; i < parameters.Length; i++)
            {
                list[i] = Expression.Convert(Expression.ArrayIndex(arrays, Expression.Constant(i)), parameters[i].ParameterType);
            }
            if (method.IsStatic)
            {
                MethodCallExpression methodExp = Expression.Call(method, list);
                return Expression.Lambda<ExecRoute>(methodExp, target, arrays).Compile();
            }
            else
            {
                Expression exception = Expression.Convert(target, method.DeclaringType);
                MethodCallExpression methodExp = Expression.Call(exception, method, list);
                return Expression.Lambda<ExecRoute>(methodExp, target, arrays).Compile();
            }
        }
        public static ExecAction GetExecAction(MethodInfo method)
        {
            ParameterExpression target = Expression.Parameter(PublicDataDic.ObjectType, "target");
            ParameterExpression arrays = Expression.Parameter(typeof(object[]), "args");
            ParameterInfo[] parameters = method.GetParameters();
            Expression[] list = new Expression[parameters.Length];
            for (int i = 0; i < parameters.Length; i++)
            {
                list[i] = Expression.Convert(Expression.ArrayIndex(arrays, Expression.Constant(i)), parameters[i].ParameterType);
            }
            if (method.IsStatic)
            {
                MethodCallExpression methodExp = Expression.Call(method, list);
                return Expression.Lambda<ExecAction>(methodExp, target, arrays).Compile();
            }
            else
            {
                Expression exception = Expression.Convert(target, method.DeclaringType);
                MethodCallExpression methodExp = Expression.Call(exception, method, list);
                return Expression.Lambda<ExecAction>(methodExp, target, arrays).Compile();
            }
        }
        public static ExecFunc GetExecFunc(MethodInfo method)
        {
            ParameterExpression target = Expression.Parameter(PublicDataDic.ObjectType, "target");
            ParameterExpression arrays = Expression.Parameter(typeof(object[]), "args");
            ParameterInfo[] parameters = method.GetParameters();
            Expression[] list = new Expression[parameters.Length];
            for (int i = 0; i < parameters.Length; i++)
            {
                list[i] = Expression.Convert(Expression.ArrayIndex(arrays, Expression.Constant(i)), parameters[i].ParameterType);
            }
            if (method.IsStatic)
            {
                MethodCallExpression methodExp = Expression.Call(method, list);
                return Expression.Lambda<ExecFunc>(Expression.Convert(methodExp, PublicDataDic.ObjectType), target, arrays).Compile();
            }
            else
            {
                Expression exception = Expression.Convert(target, method.DeclaringType);
                MethodCallExpression methodExp = Expression.Call(exception, method, list);
                return Expression.Lambda<ExecFunc>(Expression.Convert(methodExp, PublicDataDic.ObjectType), target, arrays).Compile();
            }
        }

        private ITcpRoute _CreateRoute(string name, MethodInfo method)
        {
            if (method.ReturnType == ConfigDic.VoidType)
            {
                return new VoidFuncRoute(name, method);
            }
            else if (method.ReturnType == ConfigDic.ReturnType)
            {
                return new Route.BasicRoute(name, method);
            }
            else
            {
                return new ReturnFuncRoute(name, method);
            }
        }
        private bool _CheckFormat(MethodInfo method)
        {
            ParameterInfo[] param = method.GetParameters();
            if (param.Length == 0)
            {
                return true;
            }
            return !param.IsExists(a => a.IsOut || a.ParameterType.IsByRef);
        }
        private void _LoadRoute(MethodInfo[] methods, Type form, Type to)
        {
            if (methods.Length == 0)
            {
                return;
            }
            RpcClient.Unity.Register(form, to, to.FullName);
            methods.ForEach(b =>
            {
                if (b.DeclaringType == to && b.IsPublic && !b.IsConstructor && !b.IsVirtual)//限定 是声明该方法的类 不能是虚方法和构造函数 需是公开的方法 
                {
                    if (!this._CheckFormat(b))
                    {
                        RpcLogSystem.AddRouteLog(b, b.Name, to, "rpc.route.format.error");
                        return;
                    }
                    string name = RpcClientHelper.GetRouteName(b);
                    if (!RpcClient.Route.CheckIsExists(name))
                    {
                        ITcpRoute route = this._CreateRoute(name, b);
                        RpcClient.Route.AddRoute(route);
                    }
                    else
                    {
                        RpcLogSystem.AddRouteLog(b, name, to, "rpc.route.repeat");
                    }
                }
            });
        }

        private void _LoadMethod(Type form, Type to)
        {
            MethodInfo[] methods = to.GetMethods();
            this._LoadRoute(methods, form, to);
            if (to.BaseType.FullName != "System.Object")
            {
                this._LoadMethod(to.BaseType, to.BaseType);
            }
        }
        public void AddRoute(Assembly assembly)
        {
            if (assembly != null)
            {
                assembly.GetTypes().ForEach(a =>
                {
                    try
                    {
                        Type type = a.GetInterface(ConfigDic.RpcApiService.FullName);
                        if (type != null)
                        {
                            this._LoadMethod(ConfigDic.RpcApiService, a);
                            return;
                        }
                        RpcRouteGroup attr = a.GetCustomAttribute<RpcRouteGroup>();
                        if (attr != null)
                        {
                            this._LoadMethod(a, a);
                            return;
                        }
                        type = a.GetInterface(ConfigDic.IStreamAllot.FullName);
                        if (type != null)
                        {
                            if (RpcClient.Unity.Register(ConfigDic.IStreamAllot, a, a.FullName))
                            {
                                IStreamAllot stream = RpcClient.Unity.Resolve<IStreamAllot>(a.FullName);
                                FileUpRouteCollect.AddAllot(stream);
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        RpcLogSystem.AddRouteErrorLog( a, ErrorException.FormatError(e));
                    }
                });
            }
        }
    }
}
